#include "matrix.h"

// see matrix.h - basic constructor
template <class T>
Matrix<T>::Matrix() {
    size = 0;
    last = 0;
    elements = NULL;
}
template Matrix<int>::Matrix();

// see matrix.h - normal constructor
template <class T>
Matrix<T>::Matrix(int matrixSize) {
    size = matrixSize;
    last = 0;
    elements = NULL;
}
template Matrix<int>::Matrix(int matrixSize);

// see matrix.h - copy constructor
template <class T>
Matrix<T>::Matrix(const Matrix<T>& other) {
    size = other.size;
    last = other.last;
    elements = new T[size * size];
    for (int i = 0; i < size * size; i++) {
        elements[i] = other.elements[i];
    } 
}
template Matrix<int>::Matrix(const Matrix<int>& other);

// see matrix.h
template <class T>
Matrix<T>::~Matrix() {
    if (elements != NULL) { delete[] elements; }
}
template Matrix<int>::~Matrix();

// see matrix.h
template <class T>
void Matrix<T>::parseLines(stringstream* buffer) {
    T* elements = new T[size * size];
    T tmp = -1;
    
    for (int i = 0; i < size * size; i++) {
        *buffer >> tmp;
        elements[i] = tmp;
    }
    this->elements = elements;
}
template void Matrix<int>::parseLines(stringstream* buffer);

// see matrix.h
template <class T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T>& right) {
    size = right.size;
    last = right.last;
    elements = new T[size * size];
    for (int i = 0; i < size * size; i++) {
        elements[i] = right.elements[i];
    } 
    return *this;
}
template Matrix<int>& Matrix<int>::operator=(const Matrix<int>& right);

// see matrix.h
template <class T>
Matrix<T>& Matrix<T>::operator+=(const Matrix<T>& right) {
    for (int i=0; i<size*size; i++) {
        this->elements[i] = this->elements[i] + right.elements[i];
    }
    return *this;
}
template Matrix<int>& Matrix<int>::operator+=(const Matrix<int>& right);

// see matrix.h
template <class T>
Matrix<T> Matrix<T>::operator+(const Matrix<T>& right) const {
    Matrix<T> result = *this;
    result += right;    
    return result;
}
template Matrix<int> Matrix<int>::operator+(const Matrix<int>& right) const;

// see matrix.h
template <class T>
Matrix<T>& Matrix<T>::operator-=(const Matrix<T>& right) {
    for (int i=0; i<size*size; i++) {
        this->elements[i] = this->elements[i] - right.elements[i];
    }
    return *this;
}
template Matrix<int>& Matrix<int>::operator-=(const Matrix<int>& right);

// see matrix.h
template <class T>
Matrix<T> Matrix<T>::operator-(const Matrix<T>& right) const {
    Matrix result = *this;
    result -= right;    
    return result;
}
template Matrix<int> Matrix<int>::operator-(const Matrix<int>& right) const;

// see matrix.h
template <class T>
Matrix<T>& Matrix<T>::operator*=(const Matrix<T>& right) {
    int* dotProducts = new int[size * size]; // Contain the new dot products
    int rowIdx = 0; // First element of the current row
    int colIdx = 0;  // First element of the current column
    
    // Walk every element in self
    for (int i=0; i<size*size; i++) {
        
        // If we've going through 'size' iterations, advance the row and reset the col
        if ((i % size) == 0 && i != 0) { rowIdx += size; }        
        if (colIdx == size) { colIdx = 0; }
        
        int dotProduct = 0; // Dot-product being calculated
        
        // Must go through 'size' elements when doint dot-product
        for (int j=0; j<size; j++) {
            int x = rowIdx + j; // the current row ID 
            int y = colIdx + j*size; // the current column ID
            dotProduct += this->elements[x] * right.elements[y]; 
        }
        dotProducts[i] = dotProduct;
        colIdx++;
    }
    
    // Now replace *this values with the dotProducts
    for (int i=0; i<size*size; i++) {
        this->elements[i] = dotProducts[i];
    }
    
    delete dotProducts; // delete our temporary items
    
    return *this;
}
template Matrix<int>& Matrix<int>::operator*=(const Matrix<int>& right);

// see matrix.h
template <class T>
Matrix<T> Matrix<T>::operator*(const Matrix<T>& right) const {
    Matrix<T> result = *this;
    result *= right;    
    return result;
}
template Matrix<int> Matrix<int>::operator*(const Matrix<int>& right) const;